/**
 * 
 */
package com.ofek.eventos.db;

import java.io.IOException;

import com.ofek.eventos.db.datatypes.EventData;
import com.ofek.eventos.db.datatypes.newer.EventID;
import com.ofek.eventos.db.datatypes.newer.Request;
import com.ofek.eventos.db.datatypes.newer.RequestID;
import com.ofek.eventos.net.lks.ClientDatabaseHandle;
import com.ofek.eventos.net.lks.DistributedDatabase;
import com.ofek.eventos.net.lks.DistributedDatabaseListener;
import com.ofek.eventos.net.lks.IMap;
import com.ofek.eventos.net.lks.ISyncer;
import com.ofek.eventos.net.lks.ItemVersion;
import com.ofek.eventos.net.lks.Sync;
import com.ofek.eventos.net.lks.test.LKSDebug;
import com.ofek.eventos.net.ocsf.client.RemoteService;

/**
 * @author Ofek
 *
 */
public class ClientDB {
	private final String uid;
	private long eventIdsCreator = 0;

	private final DistributedDatabase<RequestID, Request> requestsDB;

	private final DistributedDatabase<EventID, EventData> eventsDB;
	private int requestIdsCreator;


	public boolean addEventsListener(
			final DistributedDatabaseListener<EventID, EventData> l) {
		return eventsDB.addDistributedDatabaseListener(l);
	}
	public boolean removeEventsListener(
			final DistributedDatabaseListener<EventID, EventData> l) {
		return eventsDB.removeDistributedDatabaseListener(l);
	}
	public boolean addRequestsListener(
			final DistributedDatabaseListener<RequestID, Request> l) {
		return requestsDB.addDistributedDatabaseListener(l);
	}
	public boolean removeRequestsListener(
			final DistributedDatabaseListener<RequestID, Request> l) {
		return requestsDB.removeDistributedDatabaseListener(l);
	}

	private class RequestsSyncer extends RemoteService implements ISyncer<RequestID, Request>{

		/**
		 * @param host
		 * @param port
		 */
		public RequestsSyncer(final String host, final int port) {
			super(host, port);

		}
		/* (non-Javadoc)
		 * @see com.ofek.eventos.net.lks.ISyncer#syncOut(com.ofek.eventos.net.lks.Sync)
		 */
		@Override
		public Sync<RequestID, Request> syncOut(final Sync<RequestID, Request> sync)
				throws Exception {
			return syncRequests(sync);
		}
		/**
		 * @param sync
		 * @return
		 * @throws IOException 
		 */
		private Sync<RequestID, Request> syncRequests(final Sync<RequestID,Request> sync) throws IOException {
			return (Sync) handle(sync);
		}
		/* (non-Javadoc)
		 * @see com.ofek.eventos.net.lks.ISyncer#close()
		 */
		@Override
		public void close() {
		}
	}

	private class EventsSyncer extends RemoteService implements ISyncer<EventID, EventData>{

		/**
		 * @param host
		 * @param port
		 */
		public EventsSyncer(final String host, final int port) {
			super(host, port);

		}
		/* (non-Javadoc)
		 * @see com.ofek.eventos.net.lks.ISyncer#syncOut(com.ofek.eventos.net.lks.Sync)
		 */
		@Override
		public Sync<EventID, EventData> syncOut(final Sync<EventID, EventData> sync)
				throws Exception {
			return syncEvents(sync);
		}
		/**
		 * @param sync
		 * @return
		 * @throws IOException 
		 */
		private Sync<EventID, EventData> syncEvents(final Sync<EventID, EventData> sync) throws IOException {
			return (Sync) handle(sync);
		}
		/* (non-Javadoc)
		 * @see com.ofek.eventos.net.lks.ISyncer#close()
		 */
		@Override
		public void close() {

		}
	}

	/**
	 * @param pendingSyncRequests 
	 * @param syncedRequests 
	 * @param pendingRemovalRequests 
	 * @param pendingSyncEvents 
	 * @param syncedEvents 
	 * @param pendingRemovalEvents 
	 * @param requestsDBHandle 
	 * @param eventsDBHandle 
	 * 
	 */
	public <T extends ItemVersion<RequestID,Request>> ClientDB(final String id,final String host,final int port,
			final IMap<RequestID, 	ItemVersion<RequestID,Request>> pendingSyncRequests, 
			final IMap<RequestID,	ItemVersion<RequestID,Request>> syncedRequests, 
			final IMap<RequestID,	ItemVersion<RequestID,Request>> pendingRemovalRequests, 
			final IMap<EventID, ItemVersion<EventID,EventData>> pendingSyncEvents, 
			final IMap<EventID, ItemVersion<EventID,EventData>> syncedEvents, 
			final IMap<EventID, ItemVersion<EventID,EventData>> pendingRemovalEvents) {
		uid = id;
		requestsDB = new DistributedDatabase<RequestID, Request>(uid,new ClientDatabaseHandle<RequestID, Request>(pendingSyncRequests, syncedRequests, pendingRemovalRequests), new RequestsSyncer(host, port) );
		eventsDB = new DistributedDatabase<EventID, EventData>(uid,new ClientDatabaseHandle<EventID, EventData>(pendingSyncEvents, syncedEvents, pendingRemovalEvents), new EventsSyncer(host, port) );

		requestsDB.addDistributedDatabaseListener(new DistributedDatabaseListener<RequestID, Request>() {

			@Override
			public void onItemSynced(final ItemVersion<RequestID, Request> item) {
				if ( item.item.what.equals(Request.REMOVE)  ) LKSDebug.println(uid+"," + item.item.extra);
				if ( !item.id.dest.equals(uid) ) return;
				if ( item.item.what.equals(Request.REMOVE)  ) {
					final EventID extra = (EventID) item.item.extra;
					eventsDB.initiateRemove(extra);
					LKSDebug.println("Remove requested " + item);
				} else {
					final EventData extra = (EventData) item.item.extra;
					add(extra);
					LKSDebug.println("AddOrUpdate requested " + item);
				}
			}

			@Override
			public void onItemRemoved(final ItemVersion<RequestID, Request> item) {

			}
		});
	}
	/**
	 * request add external event
	 * @param e
	 */
	public void requestAdd(final String dest,final EventData item) {
		requestsDB.initiateAddOrUpdate(new RequestID(uid, dest,requestIdsCreator++), new Request(Request.ADD_OR_UPDATE, item));
	}
	/**
	 * add a local event
	 * @param e
	 * @return 
	 */
	public EventID add(final EventData e) {
		final EventID id = new EventID(eventIdsCreator++, uid);
		eventsDB.initiateAddOrUpdate(id, e);
		return id;
	}
	/**
	 * add a local event
	 * @param e
	 */
	public void remove(final EventID e) {
		if ( e.owner.equals(uid)) 
			eventsDB.initiateRemove(e);
		else 
			requestsDB.initiateAddOrUpdate(new RequestID(uid, e.owner,requestIdsCreator++), new Request(Request.REMOVE, e));
	}
	/**
	 * 
	 */
	public void sync() {
		requestsDB.sync();
		eventsDB.sync();
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "\nRequests:\n"+requestsDB + "\nEvents:\n" + eventsDB + "\n";
	}


}
